package com.gitee.wsl.net.ssdp.description.model.xml

import com.gitee.wsl.logger.Logger
import com.gitee.wsl.logger.info
import com.gitee.wsl.net.ssdp.description.xml.XmlElement
import kotlin.properties.Delegates

object ServiceStateTable{
    /**
     * State variable defined by a UPnP Forum working committee. Each service shall have => 1 state variables.
     * If UPnP vendor differentiates service by adding additional state variables, repeat once for
     * each additional state variable
     * @param sendEvents    - Whether event messages will be generated when the value of this state variable changes.
     *                          Default value is “yes”. Non-evented state variables shall set this attribute to “no”.
     *                          For standard state variables defined by a UPnP Forum working committee, the
     *                          working committee decides whether the variable is evented and the value of the
     *                          sendEvents attribute shall not be altered by a vendor.
     *                          For non-standard state variables specified by a UPnP vendor and added to a
     *                          standard service, the vendor is allowed to decide whether the non-standard
     *                          state variable will be evented or not.
     *
     * @param multicast     - Whether event messages will be delivered using multicast eventing.
     *                          Default value is “no”. If the multicast is set to “yes”, then all events sent for this
     *                          state variable shall be unicast AND multicast.
     *                          For standard state variables defined by a UPnP Forum working committee, the working
     *                          committee decides whether the state variable is multicast and the value of the multicast
     *                          attribute shall not be altered by a vendor.
     *                          For non-standard variables specified by a UPnP vendor and added to a standard
     *                          service, the vendor is allowed to decide whether the non-standard variable will
     *                          be delivered using multicast eventing.
     * @param name          - Name of state variable. Shall not contain a hyphen character (“-”, 2D Hex in UTF-8).
     *                          First character shall be a USASCII letter (“A”-“Z”, “a”-“z”), USASCII digit (“0”-“9”),
     *                          an underscore (“_”), or a non-experimental Unicode letter or digit greater than U+007F.
     *                          Succeeding characters shall be a USASCII letter (“A”-“Z”, “a”-“z”), USASCII digit
     *                          (“0”-“9”), an underscore (“_”), a period (“.”), a Unicode combiningchar, an extender, or
     *                          a non-experimental Unicode letter or digit greater than U+007F. The first three letters
     *                          shall not be “XML” in any combination of case. Case sensitive.
     *                          For standard state variables defined by a UPnP Forum working committee, shall not begin
     *                          with “X_” nor “A_”.
     *                          For non-standard state variables specified by a UPnP vendor and added to a standard
     *                          service, shall begin with “X_”, followed by a Vendor Domain Name, followed by the
     *                          underscore character (“_”), followed by the vendor-assigned state variable name. The
     *                          vendor-assigned state variable name shall comply with the syntax rules defined above.
     *                          String. Should be < 32 characters.
     * @param dataType      - Same as data types defined by XML Schema, Part 2: Datatypes. Defined by a UPnP Forum
     *                          working committee for standard state variables; specified by UPnP vendor for extensions.
     *                          Has an allowed type attribute <type>. If the type attribute is present, the value of the
     *                          <dataType> element shall be “string”. The value of the type attribute overrides the
     *                          “string” value; it defines the data type using a fully qualified data type name
     *                          according to the conventions of XML schema and can refer to XML Schema simple types,
     *                          service-local complex types and service-local extended simple types. Service-local data
     *                          types are defined in a corresponding UPnP Service Template or they are allowed to be
     *                          vendor-specific. See also clause 2.5.1, “Defining and processing extended data types”
     *                          and clause 2.5.2, “String equivalents of extended data types”.
     *                          For example: <dataType type="xsd:byte">string</dataType>
     *                          For a state variable using an extended data type via the type attribute and containing
     *                          complex data, the <defaultValue>, <allowedValueList> and <allowedValueRange> elements
     *                          shall not be present. In such case the restrictions for the data type shall be described
     *                          in the data type schema provided in the service template document.
     *                          The <dataType> element shall have one of the following values:
     *                          *uiX* - Unsigned X Byte int. X can be 1, 2, 4, 8. Same format as int without leading sign.
     *                          *iX*  - X Byte int. X can be 1, 2, 4, 8.
     *                          *int* - Fixed point, integer number. Is allowed to have leading sign. Is allowed to
     *                                  have leading zeros, which should be ignored by the recipient. (No currency
     *                                  symbol.) (No grouping of digits to the left of the decimal, e.g., no commas.)
     *                          *r4*  - 4 Byte float. Same format as float. shall be between 3.40282347E+38 to 1.17549435E-38.
     *                          *r8*  - 4 Byte float. Same format as float. shall be between -1.79769313486232E308 and
     *                                  -4.94065645841247E-324 for negative values, and between 4.94065645841247E-324
     *                                  and 1.79769313486232E308 for positive values, i.e., IEEE 64-bit (8-Byte) double.
     *                          *number* - Same as r8
     *                          *fixed.14.4* - Same as r8 but no more than 14 digits to the left of the decimal point
     *                                          and no more than 4 to the right.
     *                          *float* - Floating point number. Mantissa (left of the decimal) and/or exponent is
     *                                    allowed to have a leading sign. Mantissa and/or exponent Is allowed to have
     *                                    leading zeros, which should be ignored by the recipient. Decimal character
     *                                    in mantissa is a period, i.e., whole digits in mantissa separated from
     *                                    fractional digits by period (“.”). Mantissa separated from exponent by “E”.
     *                                    (No currency symbol.) (No grouping of digits in the mantissa, e.g., no commas.)
     *                          *char* - Unicode string. One character long.
     *                          *string* - Unicode string. No limit on length.
     *                          *date* - Date in a subset of ISO 8601 format without time data.
     *                          *dateTime* - Date in ISO 8601 format with allowed time but no time zone.
     *                          *dateTime.tz* - Date in ISO 8601 format with allowed time and allowed time zone.
     *                          *time* - Time in a subset of ISO 8601 format with no date and no time zone.
     *                          *time.tz* - Time in a subset of ISO 8601 format with allowed time zone but no date.
     *                          *boolean* - “0” for false or “1” for true. The values “true”, “yes”, “false”, or “no” are
     *                                      deprecated and shall not be sent but shall be accepted when received.
     *                                      When received, the values “true” and “yes” shall be interpreted as true and
     *                                      the values “false” and “no” shall be interpreted as false.
     *                          *bin.base64* - MIME-style Base64 encoded binary BLOB. Takes 3 Bytes, splits them into 4
     *                                      parts, and maps each 6 bit piece to an octet. (3 octets are encoded as 4.)
     *                                      No limit on size.
     *                          *bin.hex* - Hexadecimal digits representing octets. Treats each nibble as a hex digit
     *                                      and encodes as a separate Byte. (1 octet is encoded as 2.) No limit on size.
     *                          *uri*  - Universal Resource Identifier.
     *                          *uuid* - Universally Unique ID. See clause 1.1.4, “UUID format and recommended
     *                                      generation algorithms” for the MANDATORY UUID format.
     * @param defaultValue - Expected, initial value. Defined by a UPnP Forum working committee or delegated to UPnP
     *                          vendor. shall match data type. shall satisfy <allowedValueList> or <allowedValueRange>
     *                          constraints. For a state variable using an extended data type via the type attribute and
     *                          containing complex data, the <defaultValue> element shall not be present.
     * @param allowedValueList -  Enumerates legal string values. PROHIBITED for data types other than string. At most
     *                          one of the <allowedValueRange> or <allowedValueList> elements are allowed to be
     *                          specified. Sub elements are ordered. For a state variable using an extended data type
     *                          via the type attribute and containing complex data, the <allowedValueList> element shall
     *                          not be present.
     *                          A legal value for a string variable. Defined by a UPnP Forum working committee for
     *                          standard state variables; if the UPnP Forum working committee permits it, UPnP vendors
     *                          are allowed to add vendor-specific allowed values to standard state variables. Specified
     *                          by UPnP vendor for extensions. String. shall be < 32 characters.
     * @param allowedValueRange - Defines bounds for legal numeric values; defines resolution for numeric values.
     *                          Defined only for numeric data types (i.e. integers and floats). At most one of the
     *                          <allowedValueRange> or <allowedValueList> elements are allowed to be specified. For a
     *                          state variable using an extended data type via the type attribute and containing complex
     *                          data, the <allowedValueRange> element shall not be present.
     */
    data class StateVariable(
        val sendEvents: String?,
        val multicast: String?,
        val name: String,
        val dataType: String,
        val defaultValue: String?,
        val allowedValueList: List<String>,
        val allowedValueRange: ValueRange?
    ) {

        object AllowedValueList {
            fun fromXml(xml: XmlElement): List<String> = xml.children?.mapNotNull(XmlElement::getOrNull) ?: emptyList()
        }

        /**
         * Facilitates flexible deserialization and conditional validation
         */
        companion object {
            fun fromXml(xml: XmlElement): StateVariable {
                if (xml.tag != "stateVariable") {
                    throw IllegalArgumentException("Unexpected element ${xml.tag} in StateVariable")
                }
                var sendEventsS: String? = null
                var multicastS: String? = null
                var nameS by Delegates.notNull<String>()
                var dataTypeS by Delegates.notNull<String>()
                var defaultValueS: String? = null
                var allowedValueListO = emptyList<String>()
                var allowedValueRangeO: ValueRange? = null

                xml.children?.forEach {
                    when (it.tag) {
                        "sendEvents" -> sendEventsS = it.getOrNull<String>()
                        "multicast" -> multicastS = it.getOrNull<String>()
                        "name" -> nameS = it.get()
                        "dataType" -> dataTypeS = it.get()
                        "defaultValue" -> defaultValueS = it.getOrNull<String>()
                        "allowedValueList" -> allowedValueListO = AllowedValueList.fromXml(it)
                        "allowedValueRange" -> allowedValueRangeO = ValueRange.Companion.fromXml(it)
                        else -> Logger.info("Unexpected tag ${it.tag}")
                    }
                }
                return StateVariable(
                    sendEvents = sendEventsS,
                    multicast = multicastS,
                    name = nameS,
                    dataType = dataTypeS,
                    defaultValue = defaultValueS,
                    allowedValueList = allowedValueListO,
                    allowedValueRange = allowedValueRangeO
                )
            }
        }
    }

    fun fromXml(xml: XmlElement): List<StateVariable> = xml.children?.map(StateVariable::fromXml) ?: emptyList()
}